package au.edu.adelaide.mips_soft_core.board_interface_pc.ui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.FileInputStream;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;

import au.edu.adelaide.mips_soft_core.board_interface_pc.core.Core;
import au.edu.adelaide.mips_soft_core.board_interface_pc.core.CoreListener;
import au.edu.adelaide.mips_soft_core.board_interface_pc.core.Device;
import au.edu.adelaide.mips_soft_core.board_interface_pc.core.DeviceListener;

public class UploadMemoryPanel extends JPanel implements ActionListener, ItemListener, CoreListener, DeviceListener{
	
	private String empty_string = "<Empty>";
	
	
	JComboBox<String> memoryModuleSelector = new JComboBox<String>();
	
	JButton selectFileButton = new JButton("Select Binary File");
	
	JRadioButton leaveByteOrder = new JRadioButton("Leave");
	JRadioButton swapByteOrder = new JRadioButton("Swap");
	
	ButtonGroup group = new ButtonGroup();
	{
		group.add(leaveByteOrder);
		group.add(swapByteOrder);
		swapByteOrder.setSelected(true);
	}
	
	JButton downloadButton = new JButton("UPLOAD!");
	
	File file = null;
	
	
	Device connectedDevice = null;
	
	int memSize = -1;
	
	JLabel availableSize = new JLabel();
	JLabel freeSpace = new JLabel();
	
	public UploadMemoryPanel(){
		super(new GridBagLayout());
		addComponent(this, new JLabel("Memory Module"), 0, 0, 1, 1);
		addComponent(this, memoryModuleSelector, 1, 0, 1, 1);
		addComponent(this, new JLabel("Available Size"), 0, 1, 1, 1);
		addComponent(this, availableSize, 1, 1, 1, 1);
		
		addComponent(this, selectFileButton, 0, 2, 2, 1);
		
		addComponent(this, new JLabel("Free Space Available"), 0, 3, 1, 1);
		addComponent(this, freeSpace, 1, 3, 1, 1);
		
		JPanel panel = new JPanel();
		addComponent(this, panel, 0, 4, 2, 1);
		panel.setBorder(BorderFactory.createTitledBorder("Byte Order"));
		panel.add(leaveByteOrder);
		panel.add(swapByteOrder);
		
		addComponent(this, new JLabel("Fill Empty Space With 0x"), 0, 5, 1, 1);
		addComponent(this, new JTextField("FFFFFFFF"){{this.setEnabled(false);}}, 1, 5, 1, 1);
		addComponent(this, downloadButton, 0, 6, 2, 1);
		updateMemoryModules();
		this.downloadButton.addActionListener(this);
		this.selectFileButton.addActionListener(this);
		this.memoryModuleSelector.addItemListener(this);
		Core.getCore().addCoreListener(this);
		
	}
	
	private void addComponent(JPanel panel, JComponent component, int x, int y, int width, int height){
		 GridBagConstraints c = new GridBagConstraints();
		 c.gridx = x;
		 c.gridy = y;
		 c.gridheight = height;
		 c.gridwidth = width;
		 panel.add(component,  c);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == this.selectFileButton){
			JFileChooser jfc = new JFileChooser("Select File");
			String filePath = Preferences.getValue("UploadLastFilePath");
			if(filePath != null){
				File f = new File(filePath);
				if(f.exists()){
					jfc.setSelectedFile(f);
				}
			}
			int result = jfc.showOpenDialog(this);
			if(result != JFileChooser.APPROVE_OPTION){
				return;
			}
			Preferences.setValue("UploadLastFilePath", jfc.getSelectedFile().getPath());
			this.file = jfc.getSelectedFile();
			this.changeSelected(this.memoryModuleSelector.getSelectedIndex());
		}else if(e.getSource() == this.downloadButton){
			attemptDownload();
		}
	}
	
	void attemptDownload(){
		if(this.file == null){
			JOptionPane.showMessageDialog(this, "No file selected", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}else if(!this.file.exists()){
			JOptionPane.showMessageDialog(this, "Empty file selected", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}
		try {
			FileInputStream in = new FileInputStream(this.file);
			long fileSize = this.file.length();
			byte selectedMem = (byte)this.memoryModuleSelector.getSelectedIndex();
			int maxSize = connectedDevice.getSizeOfMemoryModule(selectedMem);
			System.out.println("uploading to memory mod " + selectedMem + " " + maxSize + " : " + fileSize);
			if(fileSize % 4 != 0){
				fileSize = fileSize - (fileSize % 4);
			}
			if(fileSize == 0){
				System.out.println("Empty file");
				return;
			}
			if(fileSize > maxSize * 4){
				fileSize = maxSize * 4;
			}
			byte[] buffer = new byte[(int)fileSize];
			int amount = 0;
			int read = in.read(buffer, 0, buffer.length);
			while(read != -1){
				amount += read;
				if(amount != buffer.length){
					read = in.read(buffer, amount, buffer.length - amount);
				}else{
					break;
				}
			}
			
			if(this.swapByteOrder.isSelected()){
				for(int i = 0; i < fileSize; i+=4){
					byte a = buffer[i];
					buffer[i] = buffer[i + 3];
					buffer[i + 3] = a;
					a = buffer[i+ 1];
					buffer[i + 1] = buffer[i + 2];
					buffer[i + 2] = a;
				}
			}
			connectedDevice.uploadToBoardMemory(selectedMem, buffer);
			in.close();
			Core.getCore().getLogger().write("DONE WRITING TO BOARD", Core.LOG_LEVEL_MESSAGE);
		} catch (Exception e1) {
			e1.printStackTrace();
			JOptionPane.showMessageDialog(this, e1.getMessage(), "Something Went wrong", JOptionPane.ERROR_MESSAGE);
		}
	}
	
	void changeSelected(int index){
		
		if(index == -1){
			disablePanel();
			return;
		}
		String selected = this.memoryModuleSelector.getItemAt(index);
		if(selected == empty_string){
			disablePanel();
			return;
		}
		enablePanel();
		this.memSize = this.connectedDevice.getSizeOfMemoryModule(Byte.parseByte(selected));
		this.availableSize.setText(this.memSize + " words");
		int fileWordLength = 0;
		if(this.file != null){
			fileWordLength = (int)(this.file.length() / 4);
		}else{
			this.downloadButton.setEnabled(false);
		}
		this.freeSpace.setText((memSize - fileWordLength) + " words");
	}
	
	void disablePanel(){
		this.availableSize.setText("-");
		this.downloadButton.setEnabled(false);
		this.selectFileButton.setEnabled(false);
	}
	
	void enablePanel(){
		this.downloadButton.setEnabled(true);
		this.selectFileButton.setEnabled(true);
	}
	
	void updateMemoryModules(){
		this.memoryModuleSelector.removeAllItems();
		if(this.connectedDevice == null){
			this.memoryModuleSelector.addItem(empty_string);
			return;
		}
		for(int i = 0; i < this.connectedDevice.getNumberOfMemoryModules(); i++){
			this.memoryModuleSelector.addItem(Integer.toString(i));
		}
	}

	@Override
	public void itemStateChanged(ItemEvent e) {
		if(e.getStateChange() == ItemEvent.SELECTED){
			this.changeSelected(this.memoryModuleSelector.getSelectedIndex());
		}
		
	}

	@Override
	public void deviceListChanged() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void deviceConnected(Device device) {
		this.connectedDevice = device;
		device.addDeviceListener(this);
		
	}

	@Override
	public void deviceDisconnected(Device device) {
		this.connectedDevice = null;
		device.removeListener(this);
	}

	@Override
	public void error(String message) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void boardDiagnosed() {
		updateMemoryModules();
	}

	@Override
	public void modeChanged() {
		// TODO Auto-generated method stub
		
	}

	


}
